package cn.buk.tms.eotms.service;

import cn.buk.tms.eotms.dao.BillDao;
import cn.buk.tms.eotms.dto.SettlementReportDto;
import cn.buk.tms.eotms.dto.SettlementReportHotelDto;
import cn.buk.tms.eotms.dto.SettlementReportTrainDto;
import cn.buk.tms.eotms.dto.SettlementReportVasDto;
import cn.buk.tms.eotms.entity.*;
import cn.buk.tms.eotms.report.dto.ReportTicketChangeDto;
import cn.buk.tms.eotms.report.dto.ReportTicketDetailDto;
import cn.buk.tms.eotms.report.dto.ReportTicketDto;
import cn.buk.tms.eotms.report.dto.ReportTicketRefundDto;
import cn.buk.tms.eotms.response.SettlementSheetResponseDto;
import cn.buk.tms.eotms.sc.SearchBillInfoCriteria;
import cn.buk.tms.eotms.sc.SearchSettlementCriteria;
import cn.buk.tms.eotms.util.BillUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import static java.util.stream.Collectors.toList;

/**
 * @author yfdai
 */
@Service
public class BillServiceImpl extends BaseAbstractService implements BillService {

  @Autowired
  private BillDao billDao;

  @Override
  public List<BillInfo> searchBills(final int userId, SearchBillInfoCriteria sc) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);

    return billDao.searchBills(ownerId, customerId, sc);
  }

  @Override
  public BillInfo searchBillInfoDetail(final int userId, final int id) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);

    BillInfo billInfo  = billDao.getBillInfo(id);

    if (billInfo == null) {
      return null;
    }

    if (billInfo.getEnterpriseId() != ownerId || billInfo.getCustomer().getId() != customerId) {
      return null;
    }

    if (BillInfo.BILL_TYPE_FLIGHT.equals(billInfo.getBillType())) {
      // flight ticket
      List<BillFlightTicket> tickets = billDao
          .searchBillFlightTickets(ownerId, billInfo.getBillNum());
      billInfo.setTickets(tickets);

    } else if (BillInfo.BILL_TYPE_FLIGHT_REFUND.equalsIgnoreCase(billInfo.getBillType())) {
      List<BillFlightRefundTicket> refundTickets = billDao
          .searchBillFlightRefundTickets(ownerId, billInfo.getBillNum());

      for (BillFlightRefundTicket t : refundTickets) {
        BillUtil.processRouteDetail(t.getRemark(), t.getFlights());
      }

      billInfo.setRefundTickets(refundTickets);

    } else if (BillInfo.BILL_TYPE_FLIGHT_CHANGE.equalsIgnoreCase(billInfo.getBillType())) {
      //改签
      List<BillChangeOrder> orders = billDao
          .searchBillChangeOrders(ownerId, billInfo.getBillNum());

      for (BillChangeOrder t : orders) {
        BillUtil.processRouteDetail(t.getRemark(), t.getFlights());
      }

      billInfo.setChangeOrders(orders);

    } else if (BillInfo.BILL_TYPE_TRAIN.equals(billInfo.getBillType())) {
      // train ticket
      List<BillTrainTicket> tickets = billDao
          .searchBillTrainTickets(ownerId, billInfo.getBillNum());
      billInfo.setTrainTickets(tickets);

    } else if (BillInfo.BILL_TYPE_HOTEL.equals(billInfo.getBillType())) {
      //hotel booking
      List<BillHotelBooking> hotelBookings = billDao
          .searchBillHotelBookings(ownerId, billInfo.getBillNum());
      billInfo.setHotelBookings(hotelBookings);

    } else if (BillInfo.BILL_TYPE_VAS.equalsIgnoreCase(billInfo.getBillType())) {
      List<BillVasOrder> vasOrders = billDao
          .searchBillVasOrders(ownerId, billInfo.getBillNum());
      billInfo.setVasOrders(vasOrders);
    }

    return billInfo;
  }

  @Override
  public List<SettlementSheetResponseDto> searchSettlements(int userId, SearchSettlementCriteria sc) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);

    List<SettlementSheet> list = billDao.searchSettlements(ownerId, customerId, sc);

    List<SettlementSheetResponseDto> dtos = new ArrayList<>();
    for(SettlementSheet settlementSheet: list) {
      dtos.add(SettlementSheetResponseDto.createdBy(settlementSheet));
    }

    return dtos;
  }

  @Override
  public SettlementReportDto searchSettlementReport(final int userId, final int id) {
    final int ownerId = getOwnerIdByUserId(userId);
    final int customerId = getCustomerIdByUserId(userId);


    SettlementSheet ss = billDao.getSettlementSheetById(id);
    if (ss == null || ss.getEnterpriseId() != ownerId || ss.getCustomer().getId() != customerId) {
      return null;
    }

    SettlementReportDto dto = SettlementReportDto.createBySettlement(ss);

    //机票
    List<ReportTicketDto> ticketDtoList = prepareSettlementReportTickets(ownerId,
            ss.getSettlementNo());

    //退票
    List<ReportTicketRefundDto> refunds = prepareSettlementReportRefunds(ownerId,
            ss.getSettlementNo(), ticketDtoList);

    //改签
    List<ReportTicketChangeDto> changes = prepareSettlementReportChanges(ownerId,
            ss.getSettlementNo(), ticketDtoList);

    //服务
    List<BillVasOrder> vasOrders = billDao
            .searchVasOrdersBySettlementNo(ownerId, ss.getSettlementNo());
//    vasOrders = vasOrders.stream().filter(t -> t.getTotal() != 0).collect(toList());
    List<SettlementReportVasDto> vasDtos = new ArrayList<>();
    for (BillVasOrder vasOrder : vasOrders) {
      SettlementReportVasDto dto1 = SettlementReportVasDto.createByBillVas(vasOrder);
      vasDtos.add(dto1);
    }

    //酒店
    List<BillHotelBooking> hotels = billDao
            .searchHotelOrdersBySettlementNo(ownerId, ss.getSettlementNo());
    List<SettlementReportHotelDto> hotelDtos = new ArrayList<>();
    for (BillHotelBooking hotelOrder : hotels) {
      SettlementReportHotelDto dto1 = SettlementReportHotelDto.createdBy(hotelOrder);
      hotelDtos.add(dto1);
    }

    //火车票
    List<BillTrainTicket> trains = billDao
            .searchTrainTicketsBySettlementNo(ownerId, ss.getSettlementNo());
    List<SettlementReportTrainDto> trainOrders = new ArrayList<>();
    for (BillTrainTicket trainOrder : trains) {
      SettlementReportTrainDto dto1 = SettlementReportTrainDto.createdBy(trainOrder);
      trainOrders.add(dto1);
    }

//    List<Airport> airports = basicDataDao.searchAirport(null);
    //处理机场中文名
    for (ReportTicketDto t : ticketDtoList) {
//      processAirportName1(t.getDetails(), airports);
//      cleanFakeTicketNo(t);
    }

    for (ReportTicketChangeDto t : changes) {
//      changesprocessAirportName1(t.getDetails(), airports);
//      cleanFakeTicketNo(t);
    }

    for (ReportTicketRefundDto t : refunds) {
//      processAirportName1(t.getDetails(), airports);
//      cleanFakeTicketNo(t);
    }

    for (SettlementReportVasDto t : vasDtos) {
//      processAirportName1(t.getDetails(), airports);
    }

    dto.setTickets(ticketDtoList);
    dto.setRefunds(refunds);
    dto.setChanges(changes);
    dto.setVasOrders(vasDtos);
    dto.setHotels(hotelDtos);
    dto.setTrains(trainOrders);

    return dto;
  }

  private void cleanFakeTicketNo(ReportTicketDto t) {
    if (t.getTicketNo() != null && t.getTicketNo().contains("00000")) {
      t.setTicketNo("虚拟票号");
    }
  }

  /**
   * 准备结算单报表的机票部分
   */
  private List<ReportTicketDto> prepareSettlementReportTickets(final int ownerId,
                                                               final String settlementNo) {
    //机票
    List<BillFlightTicket> tickets = billDao
            .searchTicketsBySettlementNo(ownerId, settlementNo);

    List<ReportTicketDto> ticketDtoList = new ArrayList<>();
    for (BillFlightTicket ticket : tickets) {
      ReportTicketDto dto1 = ReportTicketDto.createReportTicketDto(ticket);
      ticketDtoList.add(dto1);
    }

    // 按订单日期、乘机人姓名、出发日期排序
    ticketDtoList = ticketDtoList.stream()
            .sorted(Comparator.comparing(ReportTicketDto::getOrderDate)
                    .thenComparing(ReportTicketDto::getPassengerName)
                    .thenComparing(ReportTicketDto::getDdate))
            .collect(toList());

    return ticketDtoList;
  }


  /**
   * 准备结算单报表的退票部分（退票部分：如果票号在机票部分也出现了，则将出票和退票的票号内容放在一起）
   */
  private List<ReportTicketRefundDto> prepareSettlementReportRefunds(final int ownerId,
                                                                     final String settlementNo, List<ReportTicketDto> tickets) {
    List<BillFlightRefundTicket> refunds0 = billDao
            .searchRefundsBySettlementNo(ownerId, settlementNo);
    for (BillFlightRefundTicket t : refunds0) {
      BillUtil.processRouteDetail(t.getRemark(), t.getFlights());
    }

    List<ReportTicketRefundDto> refunds = new ArrayList<>();
    for (BillFlightRefundTicket refundOrder : refunds0) {
      ReportTicketRefundDto dto1 = ReportTicketRefundDto.createReportRefundDto(refundOrder);
      refunds.add(dto1);
    }

    //处理机场中文名
    for(ReportTicketRefundDto refundDto: refunds) {
      for(ReportTicketDetailDto detailDto: refundDto.getDetails()) {
        if (detailDto.getDportName() == null) {
          detailDto.setDportName(getAirportName(detailDto.getDport()));
        }
        if (detailDto.getAportName() == null) {
          detailDto.setAportName(getAirportName(detailDto.getAport()));
        }
      }
    }


    refunds = refunds.stream().sorted(Comparator.comparing(ReportTicketRefundDto::getTicketNo))
            .collect(toList());

    //如果同时包含出票和退票信息，则放置在一起
    boolean bContinue = true;
    while (bContinue) {
      bContinue = false;
      ReportTicketRefundDto refundTicketRemoved = null;

      for (ReportTicketRefundDto refundTicket : refunds) {
        for (ReportTicketDto ticketDto : tickets) {
          if (ticketDto.getTicketNo().equalsIgnoreCase(refundTicket.getTicketNo()) &&
                  ticketDto.getBalanceCode().equalsIgnoreCase(refundTicket.getBalanceCode())) {
            bContinue = true;

            refundTicketRemoved = refundTicket;

            int idx = tickets.indexOf(ticketDto) + 1;
            insertReportTickets(tickets, refundTicket, 1, idx);

            break;
          }
        }

        if (refundTicketRemoved != null) {
          break;
        }
      }

      if (refundTicketRemoved != null) {
        refunds.remove(refundTicketRemoved);
      }
    }

    return refunds;
  }

  private void insertReportTickets(List<ReportTicketDto> tickets, ReportTicketDto inserted,
                                   final int ticketType, final int insertIndex) {
    ReportTicketDto ticketDto1 = new ReportTicketDto();
    BeanUtils.copyProperties(inserted, ticketDto1);

    ticketDto1.setTicketType(ticketType); // 退票
    ticketDto1.setDetails(null);

    for (ReportTicketDetailDto fltInfo : inserted.getDetails()) {
      ReportTicketDetailDto detailDto = new ReportTicketDetailDto();
      BeanUtils.copyProperties(fltInfo, detailDto);

      ticketDto1.getDetails().add(detailDto);
    }

    if (insertIndex < tickets.size()) {
      tickets.add(insertIndex, ticketDto1);
    } else {
      tickets.add(ticketDto1);
    }
  }

  /**
   * 准备结算单报表的改签部分 （改签部分：如果票号在机票部分也出现了，则将出票和改签的票号内容放在一起）
   *
   * @param ownerId 企业id
   * @param settlementNo 结算单号
   * @param tickets 已有的机票列表
   */
  private List<ReportTicketChangeDto> prepareSettlementReportChanges(final int ownerId,
                                                                     final String settlementNo, List<ReportTicketDto> tickets) {
    final List<BillChangeOrder> changes0 = billDao
            .searchChangesBySettlementNo(ownerId, settlementNo);
    for (BillChangeOrder t : changes0) {
      BillUtil.processRouteDetail(t.getRemark(), t.getFlights());
    }

    List<ReportTicketChangeDto> changeDtos = new ArrayList<>();
    for (BillChangeOrder changeOrder : changes0) {
      ReportTicketChangeDto dto1 = ReportTicketChangeDto.createReportChangeDto(changeOrder);
      changeDtos.add(dto1);
    }

    //处理机场中文名
    for(ReportTicketChangeDto changeDto: changeDtos) {
      for(ReportTicketDetailDto detailDto: changeDto.getDetails()) {
        if (detailDto.getDportName() == null) {
          detailDto.setDportName(getAirportName(detailDto.getDport()));
        }
        if (detailDto.getAportName() == null) {
          detailDto.setAportName(getAirportName(detailDto.getAport()));
        }
      }
    }

    changeDtos = changeDtos.stream()
            .sorted(Comparator.comparing(ReportTicketChangeDto::getTicketNo)).collect(toList());
    //如果同时包含出票和改签，则放置在一起
    boolean bContinue = true;
    while (bContinue) {
      bContinue = false;
      ReportTicketChangeDto changeRemoved = null;

      for (ReportTicketChangeDto changeDto : changeDtos) {
        for (ReportTicketDto ticketDto : tickets) {
          if (ticketDto.getTicketNo().equalsIgnoreCase(changeDto.getTicketNo()) &&
                  ticketDto.getBalanceCode().equalsIgnoreCase(changeDto.getBalanceCode())) {
            bContinue = true;

            changeRemoved = changeDto;

            int idx = tickets.indexOf(ticketDto) + 1;
            insertReportTickets(tickets, changeDto, 2, idx);

            break;
          }
        }

        if (changeRemoved != null) {
          break;
        }
      }

      if (changeRemoved != null) {
        changeDtos.remove(changeRemoved);
      }
    }

    return changeDtos;
  }


}
